package wsserver

import (
	"encoding/json"
	"fmt"
	"sync"

	"github.com/gorilla/websocket"
	. "starsine.cn/appTpl/cmn/defs"
	"starsine.cn/appTpl/cmn/logs"
)

// 客户端读写消息
type wsMessage struct {
	messageType int
	data        []byte
}

type WsServer struct {
	conn   *websocket.Conn
	msgQue chan *wsMessage

	wsWirteMutex sync.Mutex
	sync.RWMutex

	done       chan struct{}
	msgHandler func(conn *WsServer, msg []byte)

	appCtxt *AppContext
}

func (p *WsServer) String() string {
	return fmt.Sprintf("client=%s: conn=%p, WsServer=%p", p.conn.RemoteAddr().String(), p.conn, p)
}

func CreateWsServer(appCtxt *AppContext, conn *websocket.Conn) *WsServer {
	p := &WsServer{
		conn:       conn,
		msgQue:     make(chan *wsMessage, 1000),
		done:       make(chan struct{}),
		msgHandler: nil,
		appCtxt:    appCtxt,
	}

	return p
}

func (p *WsServer) Start() {
	go p.msgHandle()
	go p.readProc()
}

func (p *WsServer) Close() {
	p.RWMutex.Lock()
	defer p.RWMutex.Unlock()

	if p.conn != nil {
		p.conn.Close()
		p.conn = nil
	}
}

func (p *WsServer) readProc() {
	for {
		msgType, msg, err := p.conn.ReadMessage()
		if err != nil {
			logs.Errorf("wsserver ReadMessage fail: %s", err)
			close(p.done)
			return
		}

		p.msgQue <- &wsMessage{
			messageType: msgType,
			data:        msg,
		}
		logs.Debugf("wsserver recv: %s", msg)
	}
}

func (p *WsServer) msgHandle() {
	logs.Infof("wsserver msgHandle routine start")
	p.appCtxt.Wg.Add(1)
	defer func() {
		logs.Warnf("wsserver msgHandle routine exit")
		p.appCtxt.Wg.Done()
	}()

	for {
		select {
		case <-p.done:
			p.Close()
		case msg := <-p.msgQue:
			if p.msgHandler != nil {
				p.msgHandler(p, msg.data)
			}
			logs.Debugf("msgHandle proc msg: type=%d, data=%s", msg.messageType, string(msg.data))
		case <-p.appCtxt.Ctx.Done():
			logs.Warnf("wsserver msgHandle routine ctxt done to quit")
			return
		}
	}
}

func (p *WsServer) WriteMessage(messageType int, data []byte) error {
	p.RWMutex.RLock()
	defer p.RWMutex.RUnlock()
	if p.conn != nil {
		p.wsWirteMutex.Lock()
		err := p.conn.WriteMessage(messageType, data)
		p.wsWirteMutex.Unlock()
		if err != nil {
			logs.Warnf("wsserver.WriteMessage WriteMessage fail: %s", err)
			return fmt.Errorf("wsserver.WriteMessage WriteMessage fail: %s", err)
		}
	} else {
		return fmt.Errorf("wsserver.WriteMessage WriteMessage fail: %s", "p.conn==nil")
	}

	return nil
}

func (p *WsServer) SendMessage(v interface{}) error {
	p.RWMutex.RLock()
	defer p.RWMutex.RUnlock()
	if p.conn != nil {
		d, err := json.Marshal(v)
		if err != nil {
			logs.Errorf("WsServer.SendMessage to Marshal fail: %+v", v)
			return fmt.Errorf("WsServer.SendMessage to Marshal fail: %+v", v)
		}

		p.wsWirteMutex.Lock()
		err = p.conn.WriteMessage(websocket.TextMessage, d)
		p.wsWirteMutex.Unlock()
		if err != nil {
			logs.Warnf("wsserver.SendMessage WriteMessage fail: %s", err)
		}
		return nil
	} else {
		return fmt.Errorf("WsServer.SendMessage fail: conn is nil")
	}
}

func (p *WsServer) RegisterProc(fn func(conn *WsServer, msg []byte)) {
	p.RWMutex.Lock()
	defer p.RWMutex.Unlock()

	p.msgHandler = fn
}
